package app

import (
	config2 "demo/src/config"
	"demo/src/controller"
	"demo/src/ioc"
	"fmt"
	"gopkg.in/yaml.v3"
	"log"
	"net/http"
	"os"
	"reflect"
	"strings"
	"time"
)

type CommonComponent struct {
	Bean    string
	Pointer any
	RType   reflect.Type
}

func (c *CommonComponent) PostConstructor() any {
	return c.Pointer
}
func (c *CommonComponent) BeanName() string {
	return c.Bean
}
func (c *CommonComponent) Type() reflect.Type {
	return c.RType
}

type App struct {
	Controllers []controller.Controller
	Components  []ioc.Bean
	BeanFactory *ioc.BeanFactory
	Config      config2.Config
}

func NewApp() *App {
	return &App{
		Controllers: make([]controller.Controller, 0),
		Components:  make([]ioc.Bean, 0),
		BeanFactory: ioc.NewBeanFactory(),
	}
}
func (a *App) Component(b ioc.Bean) {
	of := reflect.ValueOf(b)
	typeOf := of.Type()
	typeName := typeOf.Elem().Name()
	fieldPointer := of.Elem().FieldByName("Pointer")
	fieldPointer.Set(of)
	fieldBean := of.Elem().FieldByName("Bean")
	fieldBean.SetString(typeName)
	fieldRType := of.Elem().FieldByName("RType")
	fieldRType.Set(reflect.ValueOf(typeOf))
	a.Components = append(a.Components, b)
}
func (a *App) RegService(b ioc.Bean) {
	a.Components = append(a.Components, b)
}
func (a *App) Controller(c controller.Controller) {
	a.Component(c)
	a.Controllers = append(a.Controllers, c)
}
func (a *App) RegController(c controller.Controller) {
	a.Controllers = append(a.Controllers, c)
	a.Components = append(a.Components, c)
}

type Router struct {
	routerMap             map[string]map[string]controller.Handle
	staticPrefixHandleMap []staticPrefixHandle
}

type prefixHandle struct {
	varName string
	handle  controller.Handle
}

type staticPrefixHandle struct {
	PrefixLen int
	PathMap   map[string]prefixHandle
}

func (a *App) buildRouter() *Router {
	routerMap := make(map[string]map[string]controller.Handle)
	prefixMaps := make([]staticPrefixHandle, 0)
	//log.Println(len(a.Controllers))
	for _, c := range a.Controllers {
		thfs := c.ToHandlers()
	RangeThfs:
		for _, thf := range thfs {
			log.Printf("%#v", thf)
			thf.Method = strings.ToUpper(thf.Method)
			if !strings.HasPrefix(thf.Path, "/") {
				thf.Path = "/" + thf.Path
			}
			pathVIndex := strings.Index(thf.Path, ":")
			if pathVIndex > 0 {
				for _, prefixMap := range prefixMaps {
					if prefixMap.PrefixLen == pathVIndex {
						prefixMap.PathMap[thf.Path[:pathVIndex]] = prefixHandle{
							varName: thf.Path[pathVIndex+1:],
							handle:  thf.Handle,
						}
						continue RangeThfs
					}
				}
				prefixMaps = append(prefixMaps, staticPrefixHandle{
					PrefixLen: pathVIndex,
					PathMap: map[string]prefixHandle{
						thf.Path[:pathVIndex]: {
							handle:  thf.Handle,
							varName: thf.Path[pathVIndex+1:],
						},
					},
				})
				continue
			}
			if methodMap, ok := routerMap[thf.Path]; ok {
				if _, ok := methodMap[thf.Method]; ok {
					log.Panicf("method: %s ,path: %s 的handler重复注册", thf.Method, thf.Path)
				} else {
					methodMap[thf.Method] = thf.Handle
				}
			} else {
				routerMap[thf.Path] = make(map[string]controller.Handle)
				routerMap[thf.Path][thf.Method] = thf.Handle
			}
		}
	}
	return &Router{
		routerMap:             routerMap,
		staticPrefixHandleMap: prefixMaps,
	}
}

func (a *App) Run(port int) error {
	router := a.buildRouter()
	http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		now := time.Now()
		defer func() {
			log.Printf("\n\tremote:\t%s\n\tpath:\t%s\n\tmethod:\t%s\n\tspent:\t%v\n\t", request.RemoteAddr, request.URL.Path, request.Method, time.Since(now))
		}()
		method := request.Method
		path := request.URL.Path
		if methodmap, ok := router.routerMap[path]; !ok {
			for _, handle := range router.staticPrefixHandleMap {
				if pre, ok := handle.PathMap[path[:handle.PrefixLen]]; ok {
					m := a.Config.WalkFields()
					m[pre.varName] = path[handle.PrefixLen:]
					pre.handle(m, request, writer)
					return
				}
			}
			writer.WriteHeader(404)
			writer.Write([]byte(http.StatusText(404)))
			return
		} else {
			if handle, ok := methodmap[method]; !ok {
				writer.WriteHeader(http.StatusMethodNotAllowed)
				writer.Write([]byte(http.StatusText(http.StatusMethodNotAllowed)))
				return
			} else {
				ctx := a.Config.WalkFields()
				handle(ctx, request, writer)
			}
		}
	})
	return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

var Application = NewApp()

func AppRun() {
	file, err := os.ReadFile("./application.yml")
	if err != nil {
		log.Fatalln(err)
	}
	var config config2.Config
	err = yaml.Unmarshal(file, &config)
	Application.BeanFactory.ContainerInit(Application.Components)
	Application.Config = config
	panic(Application.Run(config.Server.Port))
}
